package admin

import (
	"fmt"
	"go-web/db"
	"go-web/models"
	"go-web/utils"
	"sync"
	"time"
	"strings"
	"strconv"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
)

var SessionKey = "Lintul_xhsf_eval"
var SessionNormalKey = "Lintul_xhsf_normal_eval"
var FilterUser = func(ctx *context.Context) {
	strinterface := ctx.Input.Session(SessionKey)
	if strinterface != nil {
		strstr := strinterface.(string)
		strs := strings.Split(strstr, ":")
		if len(strs) != 4 || !GSessionUserMap.CheckSession(strstr) {
			if ctx.Input.Method() == "GET" {
				ctx.Redirect(302, "/")
			} else {
				ctx.ResponseWriter.Write(utils.JsonToBytes(-1, 0, ""))
			}
		}
	} else {
		if ctx.Input.Method() == "GET" {
			ctx.Redirect(302, "/")
		} else {
			ctx.ResponseWriter.Write(utils.JsonToBytes(-1, 0, ""))
		}
	}
}

var FilterNormal = func(ctx *context.Context) {
	strinterfice := ctx.Input.Session(SessionNormalKey)
	if strinterfice == nil {
		if ctx.Input.Method() == "GET" {
			ctx.Redirect(404, "/")
		} else {
			ctx.ResponseWriter.Write(utils.JsonToBytes(-1, 0, ""))
		}
	}
}

func init() {
	go checkUser()
	if GSessionUserMap.CheckMap == nil {
		GSessionUserMap.CheckMap = map[string]SessionUser{}
	}
	go GSessionUserMap.checkUserSession()
}

type Usertimelimit struct {
	Times		int
	LimitTime	time.Time
}

type checkMap struct {
	CheckMap 	map[string]*Usertimelimit
	Stop		bool
	mLock		sync.Mutex
}

var AllUserTimeLimit checkMap

// 错误次数超过3次无法登陆
func (au *checkMap) CanLogin(usernum string) bool {
	au.mLock.Lock()
	defer au.mLock.Unlock()
	if v, ok := au.CheckMap[usernum]; ok {
		if v.Times >= 3 {
			return false
		}
	}
	return true
}

// 将记录的错误次数和登陆事件删除
func (au *checkMap) Delete(usernum string) {
	au.mLock.Lock()
	if _, ok := au.CheckMap[usernum]; ok {
		delete(au.CheckMap, usernum)
	}
	au.mLock.Unlock()
}

// 保留错误记录
func (au *checkMap) Add(Num string) {
	au.mLock.Lock()
	if _, ok := au.CheckMap[Num]; ok {
		au.CheckMap[Num].Times++
		if au.CheckMap[Num].Times >= 3 {
			au.CheckMap[Num].LimitTime = time.Now().Add(time.Minute * 30)
		}
	} else {
		au.CheckMap[Num] = &Usertimelimit{Times: 1, LimitTime: time.Now().Add(time.Minute * 30)}
	}
	au.mLock.Unlock()
}


func (au * checkMap) Check() {
	au.mLock.Lock()
	now := time.Now()
	for k, v := range au.CheckMap {
		if v.LimitTime.Before(now) {
			delete(au.CheckMap, k)
		}
	}
	au.mLock.Unlock()
}

type SessionUser struct {
	Randstr string
	LifeOut time.Time
}

type SessionUserMap struct {
	CheckMap 	map[string]SessionUser
	Stop 		bool
	mLock		sync.Mutex
}

var GSessionUserMap SessionUserMap

func (su *SessionUserMap) AddAndUpdate(idstr, randstr string) {
	su.mLock.Lock()
	su.CheckMap[idstr] = SessionUser{Randstr: randstr, LifeOut: time.Now().Add(time.Second * 60 * 60 * 24)}
	su.mLock.Unlock()
}

func (su *SessionUserMap) CheckSession(session string) bool {
	strs1 := strings.Split(session, ":")
	if len(strs1) == 4 {
		su.mLock.Lock()
		if v, ok := su.CheckMap[strs1[1]]; ok {
			if v.Randstr == strs1[3] {
				su.mLock.Unlock()
				return true
			}
		}
		su.mLock.Unlock()
	}
	return false
}

func (su *SessionUserMap) DeleteSession(session string) {
	strs1 := strings.Split(session, ":")
	if len(strs1) == 4 {
		su.mLock.Lock()
		if _, ok := su.CheckMap[strs1[1]]; ok {
			delete(su.CheckMap, strs1[1])
		}
		su.mLock.Unlock()
	}
}

func (su *SessionUserMap) checkUserSession() {
	for !su.Stop {
		time.Sleep(time.Second * 60)
		now := time.Now()
		su.mLock.Lock()
		for k, v := range su.CheckMap {
			if v.LifeOut.Before(now) {
				delete(su.CheckMap, k)
			}
		}
		su.mLock.Unlock()
	}
}

func checkUser() {
	AllUserTimeLimit.CheckMap = make(map[string]*Usertimelimit)
	for !AllUserTimeLimit.Stop {
		time.Sleep(time.Second * 1)
		AllUserTimeLimit.Check()
	}
}

type LoginController struct {
	beego.Controller
}

func (c *LoginController) Get() {
	c.TplName = "login.html"
}

func (c *LoginController) Check() {
	num := c.GetString("num")
	pwd := c.GetString("pwd")
	if num == "" || pwd == "" || len(num) > 16 || len(pwd) != 32 {
		c.Redirect("/login", 302)
		return
	}

	if !AllUserTimeLimit.CanLogin(num) {
		c.Ctx.ResponseWriter.Write(utils.JsonToBytes(4, 0, ""))
		return
	}
	oneuser := models.User{}
	ok, _ := db.Mysql.Where("num=?", num).Select("id,pwd,status").Get(&oneuser)
	if !ok {
		c.Ctx.ResponseWriter.Write(utils.JsonToBytes(0, 0, "")) // 不存在该账户
	}

	if oneuser.Pwd != pwd {
		AllUserTimeLimit.Add(num)
		c.Ctx.ResponseWriter.Write(utils.JsonToBytes(2, 0, "")) // 密码错误
		return
	}
	fmt.Println(oneuser)
	if oneuser.Status != 0 {
		c.Ctx.ResponseWriter.Write(utils.JsonToBytes(3, 0, "")) // 账号被管理员锁定
		return
	}
	AllUserTimeLimit.Delete(num)
	randstr := utils.GetRandomString(32)
	str := num + ":" + strconv.Itoa(oneuser.Id) + ":" + strconv.FormatInt(time.Now().UnixNano(), 10) + ":" + randstr
	c.SetSession(SessionKey, str)
	GSessionUserMap.AddAndUpdate(strconv.Itoa(oneuser.Id), randstr)
	InsertOperationLog(c.Ctx, oneuser.Id, oneuser.Id, L_Login)
	c.Ctx.ResponseWriter.Write(utils.JsonToBytes(1, 0, "")) // 登录成功
}